using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

using VisualEditor.Core;

namespace VisualEditor.Objects
{
    [Serializable()]
    public class DegradationReaction : Connector
    {

        protected SpeciesGlyph _TargetSpecies;
        protected bool _IsSelected;
        protected SizeF _Dimensions;
        protected PointF _Location;

        public override object Clone()
        {
            return new DegradationReaction(this);
        }
        public override bool IsSelected
        {
            get
            {
                return _IsSelected;
            }
            set
            {
                _IsSelected = value;
            }
        }
        public override PointF Location
        {
            get
            {
                //return Utility.AddPoints(GetReferencePoint(),_Location);
                return _Location;
            }
            set
            {
                _Location = value;
                //_Location = Utility.SubtractPoints(value,GetReferencePoint());
            }
        }
        /*
        public override void MoveTo(PointF oNewPoint)
        {
            if (Program.Model.Selected.Count == 1 && Program.Model.Selected[0] == this)
                base.MoveTo(oNewPoint);
        }
         */
        public override SizeF Dimensions
        {
            get
            {
                return _Dimensions;
            }
            set
            {
                _Dimensions = value;
            }
        }
        public override RectangleF Bounds
        {
            get
            {
                if (_TargetSpecies.View == ViewType.Schematic || !_TargetSpecies.Visible) return new RectangleF(0, 0, 0, 0);
                
                // this does not work as well, we really need not the location of the target species here, but the center
                // or to be more precise the boundary that is closes to the location of the DegradationReaction
                //PointF centerOfMotion = GetReferencePoint();
                //return new RectangleF(Utility.AddPoints(centerOfMotion, _Location), _Dimensions);
                return new RectangleF(_Location, _Dimensions);
            }
            set
            {
                _Location = value.Location;
                _Dimensions = value.Size;
            }
        }
        
        public override void  Select()
        {
            _IsSelected = true;
        }

        public override void Deselect()
        {
            _IsSelected = false;
        }
        public override void Transform(System.Drawing.PointF oDirection, ref Corner oCorner)
        {
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    {
                        _Location.X += oDirection.X;
                        _Dimensions.Width -= oDirection.X;
                        _Dimensions.Height += oDirection.Y;
                        break;
                    }
                case Corner.BottomRight:
                    {
                        _Dimensions.Width += oDirection.X;
                        _Dimensions.Height += oDirection.Y;
                        break;
                    }
                case Corner.TopLeft:
                    {
                        _Location.X += oDirection.X;
                        _Location.Y += oDirection.Y;
                        _Dimensions.Width -= oDirection.X;
                        _Dimensions.Height -= oDirection.Y;
                        break;
                    }
                case Corner.TopRight:
                    {
                        _Location.Y += oDirection.Y;
                        _Dimensions.Width += oDirection.X;
                        _Dimensions.Height -= oDirection.Y;
                        break;
                    }

                default:
                    break;
            }

            if (_Dimensions.Height < 0)
            {
                _Dimensions.Height = 0;
                oCorner = FlipCornerY(oCorner);
            }
            if (_Dimensions.Width < 0)
            {
                _Dimensions.Width = 0;
                oCorner = FlipCornerX(oCorner);
            }
        }
        public Corner FlipCornerY(Corner oCorner)
        {
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    return Corner.TopLeft;
                case Corner.BottomRight:
                    return Corner.TopRight;
                case Corner.TopLeft:
                    return Corner.BottomLeft;
                case Corner.TopRight:
                    return Corner.BottomRight;
                default:
                    return Corner.None;
            }
        }
        public Corner FlipCornerX(Corner oCorner)
        {
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    return Corner.BottomRight;
                case Corner.BottomRight:
                    return Corner.BottomLeft;
                case Corner.TopLeft:
                    return Corner.TopRight;
                case Corner.TopRight:
                    return Corner.TopLeft;
                default:
                    return Corner.None;
            }
        }
        public override Corner Where(PointF oPoint)
        {
            RectangleF oBounds = Bounds;
            oBounds.Inflate(5, 5);
            RectangleF oTopLeft = new RectangleF(oBounds.Left - 5F, oBounds.Top - 5F, 10F, 10F);

            RectangleF oTopRight = new RectangleF(oBounds.Right - 5F, oBounds.Top - 5F, 10F, 10F);

            RectangleF oBottomLeft = new RectangleF(oBounds.Left - 5F, oBounds.Bottom - 5F, 10F, 10F);

            RectangleF oBottomRight = new RectangleF(oBounds.Right - 5F, oBounds.Bottom - 5F, 10F, 10F);
            
            if (oTopLeft.Contains(oPoint)) return Corner.TopLeft;
            if (oTopRight.Contains(oPoint)) return Corner.TopRight;
            if (oBottomLeft.Contains(oPoint)) return Corner.BottomLeft;
            if (oBottomRight.Contains(oPoint)) return Corner.BottomRight;
            return Corner.None;
        }

        public DegradationReaction(SpeciesGlyph node, Node waste) : base(true)
        {
            TargetSpecies = node;

            Item =new Edge(node.Item as Node, waste);
            ((Edge)_Item).Rate  = "k*" + node.ID;
            ((Edge)_Item).SetParameter("k", 0.1);
            _Location = new PointF(node.Location.X + node.Dimensions.Width + 30, node.Location.Y + node.Dimensions.Height/2 - 10);
            _Dimensions = new SizeF(20, 20);
            _oFillColorEnd = Color.DarkOrange;
            _fThickness = 2;
        }

        public DegradationReaction(DegradationReaction copy):base(true)
        {
            TargetSpecies = copy._TargetSpecies;
            
            Item =copy.Item.Clone() as Item;
            _Location = copy.Location;
            _Dimensions = copy._Dimensions;
            _oFillColorEnd = copy._oFillColorEnd;
        }

        public virtual SpeciesGlyph TargetSpecies
        {
            get { return _TargetSpecies; }
            set
            {
                if (_TargetSpecies != null && _TargetSpecies != value)
                    _TargetSpecies.Degradation = null;

                if (value == null)
                {
                    _TargetSpecies.Degradation = null;
                    _TargetSpecies = null;
                }
                else
                {
                    _TargetSpecies = value;
                    if (_TargetSpecies.Degradation != this)
                        _TargetSpecies.Degradation = (this);
                }
            }
        }
        public override List<IDrawable> Inputs
        {
            get
            {
                List<IDrawable> inputs = new List<IDrawable>();
                inputs.Add(_TargetSpecies);
                return inputs;
            }
            set
            {
                if (value.Count > 0 && value[0] is SpeciesGlyph)
                    TargetSpecies = value[0] as SpeciesGlyph;
            }
        }

        public override List<IDrawable> InputsAndOutputs
        {
            get
            {
                List<IDrawable> items = new List<IDrawable>();
                items.Add(_TargetSpecies);
                return items;
            }
        }

        static GraphicsPath _oPath = CreatePath(10,10);

        static GraphicsPath CreatePath(float w, float h)
        {
            GraphicsPath oPath = new GraphicsPath();

            oPath.AddEllipse(0, 0, w, h);
            oPath.AddLine(0, h, w, 0);
            oPath.CloseFigure();

            return oPath;
        }

        public override void Draw(System.Drawing.Graphics g)
        {
            if (_TargetSpecies.View == ViewType.Schematic || !_TargetSpecies.Visible)  return;

            Pen penUnselected = new Pen(_oFillColorEnd, _fThickness),
                penSelected = new Pen(Color.Red, _fThickness);


            GraphicsPath oPath = (GraphicsPath)_oPath.Clone();
            RectangleF pathBounds = _oPath.GetBounds();

            Matrix oTransform = new Matrix();

            oTransform.Scale(
                 _Dimensions.Width / pathBounds.Width,
                 _Dimensions.Height / pathBounds.Height, MatrixOrder.Prepend);

            //PointF centerOfMotion = GetReferencePoint();
            //PointF tLocation = Utility.AddPoints(centerOfMotion, _Location);
            PointF tLocation = _Location;

            oTransform.Translate(tLocation.X, tLocation.Y, MatrixOrder.Append);

            oPath.Transform(oTransform);

            PointF p1 = Utility.GetPointOnEdge(Bounds, new PointF(TargetSpecies.Location.X + TargetSpecies.Dimensions.Width/2, TargetSpecies.Location.Y + TargetSpecies.Dimensions.Height/2) , 5),
                   p0 = Utility.GetPointOnEdge(_TargetSpecies.Bounds, new PointF(tLocation.X + _Dimensions.Width / 2, tLocation.Y + _Dimensions.Height / 2), 5);

            double angle = Math.Atan( Math.Abs(p1.Y - p0.Y)/Math.Abs(p1.X - p0.X) );

            if (p0.X <= p1.X && p0.Y <= p1.Y)
                angle = Math.PI + angle;
            else
                if (p0.X <= p1.X && p0.Y >= p1.Y)
                    angle = Math.PI - angle;
                else
                    if (p0.X >= p1.X && p0.Y <= p1.Y)
                        angle = 2 * Math.PI - angle;
            
            if (IsSelected)
            {
                g.DrawLine(penSelected, p0, p1);
                DrawArrow(g, penSelected, p1, angle, _fThickness * 5, ArrowHeadType.Arrow);
                g.DrawPath(penSelected, oPath);

                RectangleF oBounds = Bounds;
                oBounds.Inflate(2.5F, 2.5F);
                g.DrawLine(Pens.Red, oBounds.Location.X + 3, oBounds.Location.Y, oBounds.Location.X + oBounds.Size.Width - 3, oBounds.Location.Y);
                g.DrawLine(Pens.Red, oBounds.Location.X + 3, oBounds.Location.Y + oBounds.Height, oBounds.Location.X + oBounds.Width - 3, oBounds.Location.Y + oBounds.Height);
                g.DrawLine(Pens.Red, oBounds.Location.X, oBounds.Location.Y + 3, oBounds.Location.X, oBounds.Location.Y + oBounds.Height - 3);
                g.DrawLine(Pens.Red, oBounds.Location.X + oBounds.Width, oBounds.Location.Y + 3, oBounds.Location.X + oBounds.Width, oBounds.Location.Y + oBounds.Height - 3);
                g.DrawEllipse(Pens.Red, oBounds.Left - 3, oBounds.Top - 3, 6, 6);
                g.DrawEllipse(Pens.Red, oBounds.Right - 3, oBounds.Top - 3, 6, 6);
                g.DrawEllipse(Pens.Red, oBounds.Left - 3, oBounds.Bottom - 3, 6, 6);               
                g.DrawEllipse(Pens.Red, oBounds.Right - 3, oBounds.Bottom - 3, 6, 6);

            }
            else
            {
                g.DrawLine(penUnselected, p0, p1);
                DrawArrow(g, penUnselected, p1, angle, _fThickness*5, ArrowHeadType.Arrow);
                g.DrawPath(penUnselected, oPath);
            }
        }
        PointF GetReferencePoint()
        {
            return Utility.GetPointOnEdge(_TargetSpecies.Bounds, new PointF(_Location.X + _TargetSpecies.Location.X + _TargetSpecies.Dimensions.Width / 2 + _Dimensions.Width / 2, _Location.Y + _TargetSpecies.Location.Y + _TargetSpecies.Dimensions.Height / 2 + _Dimensions.Height / 2), 0);
        }
        public override bool RemoveNode(IDrawable id)
        {
            if (id == _TargetSpecies)
            {
                Remove();
            }
            return true;
        }        

        public override SBMLExtension.EmlRenderExtension.LocalStyle ToStyle(SBMLExtension.EmlRenderExtension.RenderInformation oInfo)
        {
            SBMLExtension.EmlRenderExtension.LocalStyle oResult = new SBMLExtension.EmlRenderExtension.LocalStyle();
            SBMLExtension.EmlRenderExtension.Group oGroup = new SBMLExtension.EmlRenderExtension.Group();
            oGroup.Stroke = oInfo.AddColorDefinition(_oFillColorEnd);
            oGroup.StrokeWidth = _fThickness.ToString();

            SBMLExtension.EmlRenderExtension.Ellipses ellipse = new SBMLExtension.EmlRenderExtension.Ellipses(_Dimensions.Width / 2, _Dimensions.Height / 2);            
            ellipse.Stroke = oGroup.Stroke;
            ellipse.StrokeWidth = oGroup.StrokeWidth;
            oGroup.Children.Add(ellipse);

            SBMLExtension.EmlRenderExtension.Curve lines = new SBMLExtension.EmlRenderExtension.Curve();
            lines.Stroke = oGroup.Stroke;
            lines.StrokeWidth = oGroup.StrokeWidth;
            lines.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                new SBMLExtension.LayoutExtension.Point(0f, _Dimensions.Height),
                new SBMLExtension.LayoutExtension.Point(_Dimensions.Width, 10f)
                ));
            oGroup.Children.Add(lines);

            PointF p1 = Utility.GetPointOnEdge(Bounds, new PointF(TargetSpecies.Location.X + TargetSpecies.Dimensions.Width / 2, TargetSpecies.Location.Y + TargetSpecies.Dimensions.Height / 2), 5),
                   p0 = Utility.GetPointOnEdge(_TargetSpecies.Bounds, new PointF(_Location.X + _Dimensions.Width / 2, _Location.Y + _Dimensions.Height / 2), 5);



            lines = new SBMLExtension.EmlRenderExtension.Curve();
            lines.Stroke = oGroup.Stroke;
            lines.StrokeWidth = oGroup.StrokeWidth;
            lines.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                new SBMLExtension.LayoutExtension.Point(p1),
                new SBMLExtension.LayoutExtension.Point(p0)
                ));
            oGroup.Children.Add(lines);

            oResult.Group = oGroup;
            return oResult;
        }
       

    }
}
